
package com.tools.cleanmaster.common.imagecache;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.support.v4.util.LruCache;
import android.widget.ImageView;

import com.tools.cleanmaster.CleanApplication;
import com.tools.cleanmaster.R;
import com.tools.cleanmaster.common.thread.ThreadPool;
import com.tools.cleanmaster.utils.DiskLruCache;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.LogHelper;
import com.tools.cleanmaster.utils.Utilities;
import com.tools.cleanmaster.utils.Utils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;

/**
 * 用于图片Cache，同时使用内存LruCache和DiskLruCache 单例实现
 *
 * @author ouyang
 */
public class ImageCache {
    private static final String TAG = "ImageCache";

    private static ImageCache sInstance = null;

    private static Object mLocker = new Object();

    private LruCache<String, SoftReference<Bitmap>> mMermoryCache;// 内存缓存

    private DiskLruCache mDiskCache = null;// Disk缓存

    private static final String IMAGE_CACHE_DIR = "image_cache";

    private File mDiskCacheFolder;// Disk缓存的路径

    private static final int MERMORY_CACHE_ITEM_SIZE = 100;// 内存cache的大小

    private static final long DEFAULT_DISK_CACHE_SIZE = 20L * 1024 * 1024;// 默认DiskCache的大小:10M

    public static int TAG_IMAGE_IS_LOADING = R.mipmap.ic_launcher;// 用于标记imageView是否在载入图片

    public static int TAG_IMAGE_KEY = R.string.app_name;// 用于标记imageView载入图片的key

    private static final int DISK_CACHE_INDEX = 0;

    private HashMap<ImageView, String> mLoadingImageViewPaths;// 正在加载的任务

    private ImageCache() {
        mLoadingImageViewPaths = new HashMap<ImageView, String>();
        mMermoryCache = new LruCache<String, SoftReference<Bitmap>>(MERMORY_CACHE_ITEM_SIZE);
        initDiskCache();
    }

    private void initDiskCache() {
        if (mDiskCache == null || mDiskCache.isClosed()) {
            try {
                if (mDiskCacheFolder == null) {
                    File cacheDir = CleanApplication.getInstance().getExternalCacheDir();
                    if (cacheDir == null) {
                        return;
                    }
                    Utilities.ensureDirectory(cacheDir.getPath());
                    mDiskCacheFolder = new File(cacheDir, IMAGE_CACHE_DIR);
                    Utilities.ensureDirectory(mDiskCacheFolder.getAbsolutePath());
                }
                mDiskCache = DiskLruCache.open(mDiskCacheFolder, 1, 1, DEFAULT_DISK_CACHE_SIZE);
                    LogHelper.e(TAG, "initDiskCache end ------------ mDiskCacheFolder="
                            + mDiskCacheFolder + ",mDiskCache=" + mDiskCache);

            } catch (final Exception e) {
                    LogHelper.e(TAG, e.toString());
            }
        }
    }

    public static ImageCache getInstance() {
        synchronized (mLocker) {
            if (sInstance == null) {
                sInstance = new ImageCache();
            }
        }
        return sInstance;
    }

    /**
     * 载入图片，如果在内存则直接加载；否则异步加载，异步加载时先到DiskCache中找，如果没有则decodeFile
     *
     * @param imageView
     * @param defaultImage
     */
    public void loadImage(final ImageView imageView, Drawable defaultImage,
                          final ImageRequest request) {
        // 1.先从内存中获取，２.如果内存没有，再从disk获取，3.如果disk也没有，则将从path生成缩略图
        if (request == null || request.key == null || imageView == null) {
            return;
        }
        final String key = Utils.hashKeyForDisk(request.key);
            LogHelper.i(TAG, "loadImagePreview begin ------------ request=" + request
                    + ",imageView=" + imageView.hashCode() + ",key=" + key);
        Bitmap bitmap = null;
        SoftReference<Bitmap> softBitmap = mMermoryCache.get(key);
        if (softBitmap != null) {
            bitmap = softBitmap.get();
        }
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            imageView.setTag(TAG_IMAGE_IS_LOADING, false);
        } else {
            if (key.equals(mLoadingImageViewPaths.get(imageView))) {
                // 已经开始这个load任务，则返回
                    LogHelper.i(TAG,
                            "loadImagePreview already in task!! return!! ------------ path="
                                    + request.key + ",imageView=" + imageView.hashCode());
                return;
            }
            mLoadingImageViewPaths.put(imageView, key);
            imageView.setTag(TAG_IMAGE_IS_LOADING, true);
            imageView.setTag(TAG_IMAGE_KEY, key);
            if (defaultImage != null) {
                imageView.setImageDrawable(defaultImage);
            }
            // ２.如果内存没有，再从disk获取，3.如果disk也没有，则将从path生成缩略图
            ThreadPool.runOnPool(new Runnable() {
                @Override
                public void run() {
                    if (!(Boolean) imageView.getTag(TAG_IMAGE_IS_LOADING)
                            || !key.equals(imageView.getTag(TAG_IMAGE_KEY))) {
                        // TAG已经不一致，已经不需要此任务，则直接返回
                        mLoadingImageViewPaths.remove(imageView);
                        return;
                    }
                    Bitmap bitmapFromDiskCacheOrLoad = getBitmapFromDiskCache(key);
                    Bitmap bitmapFromFile = null;
                    if (bitmapFromDiskCacheOrLoad == null) {
                            LogHelper.i(TAG, "loadImagePreview load from file key=" + request.key);
                        bitmapFromFile = request.loadBitmap();
                        bitmapFromDiskCacheOrLoad = bitmapFromFile;
                    } else {
                            LogHelper.i(TAG, "loadImagePreview load from diskCache path="
                                    + request.key);
                    }
                    if (bitmapFromDiskCacheOrLoad != null) {
                        // 更新mermoryCache和界面
                        mMermoryCache
                                .put(key, new SoftReference<Bitmap>(bitmapFromDiskCacheOrLoad));
                        final Bitmap bitmapFromDiskCacheOrFileFinal = bitmapFromDiskCacheOrLoad;
                        ThreadPool.runOnUi(new Runnable() {

                            @Override
                            public void run() {
                                if ((Boolean) imageView.getTag(TAG_IMAGE_IS_LOADING)
                                        && key.equals(imageView.getTag(TAG_IMAGE_KEY))) {
                                    imageView.setImageBitmap(bitmapFromDiskCacheOrFileFinal);
                                }
                            }
                        });
                    }
                    // 写入到diskCache
                    if (bitmapFromFile != null) {
                        final Bitmap bitmapFromFileFinal = bitmapFromFile;
                        ThreadPool.runOnPool(new Runnable() {
                            @Override
                            public void run() {
                                addBitmapToDiskCache(key, bitmapFromFileFinal);
                            }
                        });
                    }
                    mLoadingImageViewPaths.remove(imageView);
                }
            });
        }
            LogHelper.i(TAG, "loadImagePreview end ------------ path=" + request.key);
    }

    /**
     * Get from disk cache.
     *
     * @param data Unique identifier for which item to get
     * @return The bitmap if found in cache, null otherwise
     */
    private Bitmap getBitmapFromDiskCache(String key) {
        Bitmap bitmap = null;
        LogHelper.i(TAG, "getBitmapFromDiskCache begin ------------ key=" + key);
        if (mDiskCache != null) {
            InputStream inputStream = null;
            try {
                final DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                if (snapshot != null) {
                    inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
                    if (inputStream != null) {
                        bitmap = BitmapFactory.decodeStream(inputStream);
                    }
                }
            } catch (final IOException e) {
                LogHelper.e(TAG, "getBitmapFromDiskCache - " + e);
            } finally {
                Utils.closeQuietly(inputStream);
            }
        } else {
            LogHelper.e(TAG, "WHAT?? mDiskCache == null");
        }
        LogHelper.i(TAG, "getBitmapFromDiskCache end ------------ key=" + key + ",bitmap="
                + bitmap);
        return bitmap;
    }

    /**
     * Adds a bitmap to disk cache.
     *
     * @param key    Unique identifier for the bitmap to store
     * @param bitmap The bitmap drawable to store
     */
    private void addBitmapToDiskCache(String key, Bitmap bitmap) {
        if (key == null || bitmap == null) {
            return;
        }
        LogHelper.i(TAG, "addBitmapToDiskCache begin ------------ path=" + key);
        // Add to disk cache
        if (mDiskCache != null) {
            OutputStream out = null;
            try {
                DiskLruCache.Editor editor;
                DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
                if (snapshot == null) {
                    editor = mDiskCache.edit(key);
                } else {
                    snapshot.getInputStream(DISK_CACHE_INDEX).close();
                    editor = snapshot.edit();
                }
                if (editor != null) {
                    out = editor.newOutputStream(DISK_CACHE_INDEX);
                    bitmap.compress(CompressFormat.PNG, 100, out);
                    editor.commit();
                }
                mDiskCache.flush();
                LogHelper.i(TAG, "addBitmapToDiskCache end ------------ path=" + key);
            } catch (final IOException e) {
                LogHelper.e(TAG, "IOE addBitmapToDiskCache - " + e);
            } catch (Exception e) {
                LogHelper.e(TAG, "E addBitmapToDiskCache - " + e);
            } finally {
                Utils.closeQuietly(out);
            }
        }
    }
}
